#pragma once
#include <pthread.h>
#include <queue>
#include "Task.hpp"
#include "log.hpp"

class threadpool
{
private:
    std::queue<Task> _taskQueue;
    int _num;

    pthread_mutex_t _lock;
    pthread_cond_t _cond;
    int _wakeNum;

    static threadpool *_pool;

    void Lock()
    {
        pthread_mutex_lock(&_lock);
    }
    void unLock()
    {
        pthread_mutex_unlock(&_lock);
    }
    void condWait()
    {
        pthread_cond_wait(&_cond, &_lock);
    }
    void condWake()
    {
        pthread_cond_signal(&_cond);
    }

    static void *threadRoutine(void *arg)
    {
        threadpool *tp = (threadpool *)arg;
        while (true)
        {
            tp->Lock();
            while (tp->_taskQueue.empty())
            {
                tp->_wakeNum++;
                tp->condWait();
                tp->_wakeNum--;
            }
            Task task = tp->_taskQueue.front();
            tp->_taskQueue.pop();
            tp->unLock();

            task();
        }

        return nullptr;
    }

    threadpool(int num) : _num(num), _wakeNum(0)
    {
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    bool Init()
    {
        for (int i = 0; i < _num; i++)
        {
            pthread_t tid;
            if (pthread_create(&tid, nullptr, threadRoutine, this) != 0)
            {
                Log(WARNING, "pthread create fail");
                return false;
            }
            pthread_detach(tid);
        }
        Log(INFO, "pthreadpool init success");
        return true;
    }

public:
    static threadpool *GetInstance(int num = 20)
    {
        static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
        if (_pool == nullptr)
        {
            pthread_mutex_lock(&mutex);
            if (_pool == nullptr)
            {
                _pool = new threadpool(num);
                _pool->Init();
            }
            pthread_mutex_unlock(&mutex);
        }
        return _pool;
    }

    ~threadpool()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }

    void Push(Task &task)
    {
        Lock();
        _taskQueue.push(task);
        unLock();
        condWake();
    }

    int GetwakeNum()
    {
        return _wakeNum;
    }
};

threadpool *threadpool::_pool = nullptr;